package com.z.common.pro.controller;

import com.z.common.aop.annotation.Log;
import com.z.common.aop.enums.BusinessType;
import com.z.common.pro.vo.ProDefined;
import com.z.common.pro.vo.RejectBean;
import com.z.common.web.base.BaseController;
import com.z.common.web.vo.request.PageVo;
import com.z.common.web.vo.response.Result;
import com.z.common.pro.vo.CreateVo;
import io.swagger.annotations.Api;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.history.HistoricActivityInstance;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.repository.ProcessDefinitionQuery;
import org.camunda.bpm.engine.runtime.ActivityInstance;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.springframework.beans.BeanUtils;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

@RestController
@Api(tags = {"流程定义"})
@RequestMapping("/process/defined")
public class ProDefinedController extends BaseController {
    /**
     * 基础路径
     */
    private String prefix = "system/process/defined/";
    /**
     * 工作流程服务
     */
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private HistoryService historyService;
    @Resource
    private TaskService taskService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private IdentityService identityService;
    /**
     * Describe: 获取流程定义列表视图
     * Param: modelAndView
     * Return: 流程定义列表视图
     */
    @GetMapping("main")
    public ModelAndView main() {
        return jumpPage(prefix + "main");
    }

    /**
     * 流程图查看页面
     * @return
     */
    @GetMapping("/bpmnViewXML")
    public ModelAndView bpmnViewXML(Model model, String deploymentId, String name) {
        model.addAttribute("deploymentId", deploymentId);
        model.addAttribute("name", name);
        return jumpPage(prefix + "/bpmnViewXML");
    }
    /**
     * Describe: 获取流程定义列表数据
     * Param: modelAndView
     * Return: 流程定义列表数据
     */
    @GetMapping("data")
    public Result data(PageVo pageDomain, String name) {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService
                .createProcessDefinitionQuery();
        if (StringUtils.hasText(name)) {
            processDefinitionQuery.processDefinitionNameLike(name);
        }
        List<ProcessDefinition> processDefinitions = processDefinitionQuery
                .orderByProcessDefinitionVersion()
                .asc()
                .listPage(pageDomain.start(), pageDomain.end());

        List<ProDefined> data = new ArrayList<>();

        processDefinitions.forEach(processDefinition -> {
            ProDefined defined = new ProDefined();/*
            defined.setId(processDefinition.getId());
            defined.setName(processDefinition.getName());
            defined.setVersion(processDefinition.getVersion());
            defined.setKey(processDefinition.getKey());
            defined.setResourceName(processDefinition.getResourceName());
            defined.setDiagramResourceName(processDefinition.getDiagramResourceName());
            defined.setDeploymentId(processDefinition.getDeploymentId());*/
            BeanUtils.copyProperties(processDefinition, defined);
            data.add(defined);
        });

        int count = (int) processDefinitionQuery.orderByProcessDefinitionVersion().asc().count();

        return Result.table(data, count);
    }

    /**
     * Describe: 获取流程定义列表视图
     * Param: modelAndView
     * Return: 流程定义列表视图
     */
    @GetMapping("add")
    public ModelAndView add() {
        return jumpPage(prefix + "add");
    }

    /**
     * Describe: 根据 Id 删除流程定义
     * Param: deploymentId
     * Return: Result
     */
    @PostMapping("save")
    @ResponseBody
    @Log(title = "流程定义", describe = "部署流程", type = BusinessType.ADD)
    public Result create(@RequestBody CreateVo createVo) {
       /* Map map=new HashMap();
        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        if (isMultipart) {
            // 文件上传
            // 用stream api处理，提高性能
            // System.err.println("file upload request...");
            ServletFileUpload upload = new ServletFileUpload();
            FileItemIterator fileItemIterator = upload.getItemIterator(request);
            for (; fileItemIterator.hasNext();) {
                FileItemStream item = fileItemIterator.next();
                String name = item.getFieldName();
                System.out.println(name);
                if (item.isFormField()) {
                    InputStream stream = item.openStream();
                    // 普通域
                    String value = Streams.asString(stream, "UTF-8");
                    if (map.containsKey(name)) {
                        String valueto = (String) map.get(name);
                        valueto += "," + value;
                        map.put(name, valueto);
                    } else {
                        map.put(name, value);
                    }
                    stream.close();
                } else {
                    // 文件域
                    InputStream in = item.openStream();// 获取文件流
                }
            }
        }*/
        try {
            Deployment deployment = repositoryService.createDeployment()
                    .addString(createVo.getFileName(), createVo.getXml())
                    .name(createVo.getName())
                    .deploy();
        }catch (Exception e){
            e.printStackTrace();
            return Result.error("部署失败");
        }
        return Result.success("部署成功");
    }

    /**
     * Describe: 根据 Id 删除流程定义
     * Param: deploymentId
     * Return: Result
     */
    @DeleteMapping("remove/{deploymentId}")
    public Result remove(@PathVariable String deploymentId) {
        repositoryService.deleteDeployment(deploymentId, true);
        return Result.success("删除成功");
    }

    /**
     * Describe: 获取流程资源文件
     * Param: processDefineId
     * Param: resourceName
     * Return: InputStream
     */
    private InputStream getProcessDefineResource(String processDefineId, String resourceName) {
        return repositoryService.getResourceAsStream(processDefineId, resourceName);
    }

    /**
     * Describe: 获取流程模型列表视图
     * Param: processDefineId
     * Param: resourceName
     * Return: 流程模型列表视图
     */
    @GetMapping("/resource")
    @ResponseBody
    public Result getProcessDefineResource(HttpServletResponse response,
                                           @RequestParam("deploymentId") String deploymentId, @RequestParam("name") String resourceName) {
        InputStream inputStream = getProcessDefineResource(deploymentId, resourceName);
        BufferedInputStream bis = new BufferedInputStream(inputStream);
        ByteArrayOutputStream buf = new ByteArrayOutputStream();

        int result = 0;
        try {
            result = bis.read();
            while(result != -1) {
                buf.write((byte) result);
                result = bis.read();
            }
            bis.close();
            String str = buf.toString();
            return Result.success(1, str);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bis != null){
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(buf != null){
                try {
                    buf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
        return Result.error("xml获取失败");
    }



    @GetMapping("/start")
    public Result startInstance(String key){
        System.out.println("startInstance-start");
        Map<String, Object> map = new HashMap<String, Object>(3);
//        map.put("tony", "tony");
//        map.put("joe", "joe");
//        map.put("book", "book");
        map.put("user", "demo");
        //identityService.setAuthenticatedUserId("demo");
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key, map);

        System.out.println("processInstance:"+processInstance);
        return Result.success(processInstance.getRootProcessInstanceId());
    }

    @GetMapping("/todo")
    public Result queryMyToDo(@RequestParam(value = "userId") String userId){
        System.out.println("startInstance-start");
        List<Task> taskList = new ArrayList<>();
        try {
            taskList = taskService.createTaskQuery().taskAssignee(userId).list();
            taskList.forEach(task-> System.out.println(task.toString()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.success(taskList);
    }

    @GetMapping("/taskPass")
    public String taskPass(@RequestParam(value = "taskId") String taskId){
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("user", "demo");
        taskService.complete(taskId, map);
        return "审批通过";
    }
    @GetMapping("/taskBack")
    public Result rejectToLastNode(RejectBean rejectBean) {
        //获取当前task
        Task task = taskService.createTaskQuery()
                .taskAssignee(rejectBean.getCurrentUserId()) //当前登录用户的id
                .processInstanceId(rejectBean.getProcessInstanceId())
                .singleResult();
        ActivityInstance tree = runtimeService.getActivityInstance(rejectBean.getProcessInstanceId());
        //获取所有已办用户任务节点
        List<HistoricActivityInstance> resultList = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(rejectBean.getProcessInstanceId())
                .activityType("userTask")
                .finished()
                .orderByHistoricActivityInstanceEndTime()
                .asc()
                .list();
        if(null == resultList || resultList.size() == 0){
            return Result.error("当前任务无法驳回！");
        }
        //得到第一个任务节点的id
        HistoricActivityInstance historicActivityInstance = resultList.get(0);
        String startActId = historicActivityInstance.getActivityId();
        if(startActId.equals(task.getTaskDefinitionKey())){
            return Result.error("开始节点无法驳回！");
        }
        //得到上一个任务节点的ActivityId和待办人
        Map<String, String> lastNode = getLastNode(resultList,task.getTaskDefinitionKey());
        if(null == lastNode){
            return Result.error("回退节点异常！");
        }
        String toActId = lastNode.get("toActId");
        String assignee = lastNode.get("assignee");
        //设置流程中的可变参数
        Map<String, Object> taskVariable = new HashMap<>(2);
        taskVariable.put("user", assignee);
        //taskVariable.put("formName", "流程驳回");
        taskService.createComment(task.getId(), rejectBean.getProcessInstanceId(), "驳回原因:" + rejectBean.getRejectComment());
        runtimeService.createProcessInstanceModification(rejectBean.getProcessInstanceId())
                .cancelActivityInstance(getInstanceIdForActivity(tree, task.getTaskDefinitionKey()))//关闭相关任务
                .setAnnotation("进行了驳回到上一个任务节点操作")
                .startBeforeActivity(toActId)//启动目标活动节点
                .setVariables(taskVariable)//流程的可变参数赋值
                .execute();
        return Result.success();
    }

    private String getInstanceIdForActivity(ActivityInstance activityInstance, String activityId) {
        ActivityInstance instance = getChildInstanceForActivity(activityInstance, activityId);
        if (instance != null) {
            return instance.getId();
        }
        return null;
    }

    private ActivityInstance getChildInstanceForActivity(ActivityInstance activityInstance, String activityId) {
        if (activityId.equals(activityInstance.getActivityId())) {
            return activityInstance;
        }
        for (ActivityInstance childInstance : activityInstance.getChildActivityInstances()) {
            ActivityInstance instance = getChildInstanceForActivity(childInstance, activityId);
            if (instance != null) {
                return instance;
            }
        }
        return null;
    }

    /**
     * 获取上一节点信息
     * 分两种情况：
     * 1、当前节点不在历史节点里
     * 2、当前节点在历史节点里
     * 比如，resultList={1,2,3}
     *     (1)当前节点是4，表示3是完成节点，4驳回需要回退到3
     *     (2)当前节点是2，表示3是驳回节点，3驳回到当前2节点，2驳回需要回退到1
     * 其他驳回过的情况也都包含在情况2中。
     *
     * @param resultList 历史节点列表
     * @param currentActivityId 当前待办节点ActivityId
     * @return 返回值：上一节点的ActivityId和待办人（toActId, assignee）
     */
    private static Map<String, String> getLastNode(List<HistoricActivityInstance> resultList, String currentActivityId){
        Map<String, String> backNode = new HashMap<>();
        //新建一个有序不重复集合
        LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap();
        for(HistoricActivityInstance hai : resultList){
            linkedHashMap.put(hai.getActivityId(),hai.getAssignee());
        }
        //分两种情况：当前节点在不在历史节点里面，当前节点在历史节点里
        //情况1、当前节点不在历史节点里
        int originSize = resultList.size();
        int duplicateRemovalSize = linkedHashMap.size();
        //判断历史节点中是否有重复节点
        //if(originSize == duplicateRemovalSize){
        boolean flag = false;
        for(Map.Entry entry: linkedHashMap.entrySet()){
            if(currentActivityId.equals(entry.getKey())){
                flag = true;
                break;
            }
        }
//            if(flag){
//                //当前节点在历史节点里：最后一个节点是回退节点
//                return currentNodeInHis(linkedHashMap, currentActivityId);
//            }
        if(!flag) {
            //当前节点不在历史节点里：最后一个节点是完成节点
            HistoricActivityInstance historicActivityInstance = resultList.get(originSize - 1);
            backNode.put("toActId", historicActivityInstance.getActivityId());
            backNode.put("assignee", historicActivityInstance.getAssignee());
            return backNode;
        }
        //}
        //情况2、当前节点在历史节点里（已回退过的）
        return currentNodeInHis(linkedHashMap, currentActivityId);
    }

    private static Map<String, String> currentNodeInHis(LinkedHashMap<String, String> linkedHashMap, String currentActivityId){
        //情况2、当前节点在历史节点里（已回退过的）
        Map<String, String> backNode = new HashMap<>();
        ListIterator<Map.Entry<String, String>> li = new ArrayList<>(linkedHashMap.entrySet()).listIterator();
        //System.out.println("已回退过的");
        while (li.hasNext()){
            Map.Entry<String, String> entry = li.next();
            if(currentActivityId.equals(entry.getKey())){
                li.previous();
                Map.Entry<String, String> previousEntry = li.previous();
                backNode.put("toActId",previousEntry.getKey());
                backNode.put("assignee",previousEntry.getValue());
                return backNode;
            }
        }
        return null;
    }


}
